Introduction


Download raw data from Uppmax


Setting working directory and install libraries

setwd("~/phosphoproteomics_project")
RequiredPackages <- c("BiocManager", "dplyr", "igraph", "tidyr","ggplot2","ggrepel") 
#Installs packages if not yet installed
for (i in RequiredPackages) { 
    if (!require(i, character.only = TRUE)) 
    install.packages(i)
}
BiocManager::install(version = "3.15")
library(BiocManager)
BiocManager::install("STRINGdb")
library(tidyr)
library(dplyr)
library(igraph)
library(STRINGdb)
library(ggplot2)
library(ggrepel)
remove(RequiredPackages)

Load raw data

raw_data_phospho <- read.csv(file = "~/phosphoproteomics_project/phosphoproteomics/Dataset_Phospho.csv", sep = ";")
raw_data_proteom <- read.csv(file = "~/phosphoproteomics_project/phosphoproteomics/Dataset_Proteome.csv", sep = ";")

If loop to omit na.value from all Abundance columns

Abundance_columns <- grep("Abundance", colnames(raw_data_phospho) )
for (i in Abundance_columns) {
  data_phospho <- raw_data_phospho[!(is.na(raw_data_phospho[,i])), ]
  }

Calculate Mean for Abundance and adding it as column and Ratio LIF/Control

Abundance_columns <- grep("Abundance.Control", colnames(data_phospho))
data_phospho$Abudance.Mean.Control <- rowMeans(data_phospho[,c(Abundance_columns)])
Abundance_columns <- grep("Abundance.LIF", colnames(data_phospho))
data_phospho$Abudance.Mean.LIF <- rowMeans(data_phospho[,c(Abundance_columns)])
data_phospho$Abudance.Ratio <- data_phospho$Abudance.Mean.LIF / data_phospho$Abudance.Mean.Control
remove(Abundance_columns, i)

Calculate p-values

start_for <- Sys.time()
for (row in 1:nrow(data_phospho)) {
  p_value <- t.test(data_phospho[row,10:12], data_phospho[row,13:15], alternative = "less") 
  #tests if mean of control is different from mean of LIF (case) and assigns it to list p_value
  data_phospho$Abudance.pvalue[row] <- p_value$p.value 
  #selects p.value from list and assigns it to new column"Abudance.pvalue" in the correct row
  }
end_for <- Sys.time()
end_for - start_for #We wanted to compare calculation time to compare the methods
Time difference of 1.894758 secs
#Alice quicker approach
df_copy <- dplyr::tibble(data_phospho) # Copy df
start_alice <- Sys.time()
df_copy[] <- lapply(df_copy, function(x) as.numeric(as.character(x)))
df_copy$pValues <- apply(df_copy, 1, function(x) t.test(x[10:12],x[13:15], alternative = "less", paired = F)$p.value)
end_alice <- Sys.time()
end_alice-start_alice #Proves that apply is x4 faster then loop
Time difference of 0.5370486 secs
remove(end_alice,end_for,start_alice,start_for,row,p_value, df_copy)

Adding a Volcano plot to explore the initial data

### Phospho volcano plot
string_db_volcano <- STRINGdb$new( version="11.5", species=10090, score_threshold=1, input_directory="")
# This had to be outside the volcano_phospho_plot function, because the function didnt return the string_db_volcano function that is needed also afterwards.
# Defining function to plot data
    # Makes the whole code easy to reuse.
volcano_phospho_plot <- function(phospho, number){
# Calculate LIF/Control ratio
phospho$FC <- phospho$Abudance.Mean.LIF / phospho$Abudance.Mean.Control
phospho$log2_FC <- log2(phospho$FC)
phospho$diffexpressed <- "NO"
phospho$diffexpressed[phospho$log2_FC > 1 & phospho$Abudance.pvalue < 0.05] <- "UP" # sorts out upregulated phospo values and assigns "UP" value.
phospho$diffexpressed[phospho$log2_FC < -1 & phospho$Abudance.pvalue < 0.05] <- "DOWN" # same or downregulated phospho values
phospho$delabel <- NA
phospho$delabel[phospho$diffexpressed != "NO"] <- phospho$Master.Protein.Accessions[phospho$diffexpressed != "NO"] # selecting only up or down regulated values and assigning them to the label column.
##Addition to generate "real names" not Uniprot IDs
Mapping_phospho <- string_db_volcano$map(phospho, "delabel", removeUnmappedRows = F)
Mapping_phospho <- string_db_volcano$add_proteins_description(Mapping_phospho) #Add´s protein information to STRINGid´s = preferred names are more human readable
#Volcano plot overview
ggplot(data=Mapping_phospho, aes(x=log2_FC, y=-log10(Abudance.pvalue), col=diffexpressed, label=preferred_name))+
  geom_point()+
  theme_minimal()+
  geom_vline(xintercept=c(-1, 1), col="red") +
  geom_hline(yintercept=-log10(0.05), col="red")
#saves the correct photo for both possible input tables
# number 1 or 2 has to be defined in the function input data. This is a bit clumsy solved, but there were problems with returning more than one plot out of a function.
if (number == 1) {
  ggsave("~/phosphoproteomics_project/results/Zoomed_out_Volcano.png", width = 1500, height = 1000, units = "px") # Saves plots with defined size
}
if (number == 2) {
  ggsave("~/phosphoproteomics_project/results/Zoomed_out_Volcano_for_more_phosphrylated_after_qc_and_filter.png", width = 1500, height = 1000, units = "px")
}
#Detail/Zoomed in
ggplot(data=Mapping_phospho, aes(x=log2_FC, y=-log10(Abudance.pvalue), col=diffexpressed, label=preferred_name))+
  geom_point()+
  theme_minimal()+
  geom_vline(xintercept=c(-1, 1), col="red") +
  geom_hline(yintercept=-log10(0.05), col="red") + 
  coord_cartesian(xlim = c(1,3.5), ylim = c(0.5, 6))+
  geom_label_repel()
#saves the correct photo for both possible input tables
if (number == 1) {
  ggsave("~/phosphoproteomics_project/results/Zoomed_in_Volcano.png", width = 1500, height = 1000, units = "px")
}
if (number == 2) {
ggsave("~/phosphoproteomics_project/results/Zoomed_in_Volcano_for_more_phosphrylated_after_qc_and_filter.png", width = 1500, height = 1000, units = "px")
}
}
volcano_phospho_plot(data_phospho, 1) #plots data to control
trying URL 'https://stringdb-static.org/download/protein.aliases.v11.5/10090.protein.aliases.v11.5.txt.gz'
Content type 'application/octet-stream' length 13598694 bytes (13.0 MB)
==================================================
downloaded 13.0 MB

trying URL 'https://stringdb-static.org/download/protein.info.v11.5/10090.protein.info.v11.5.txt.gz'
Content type 'application/octet-stream' length 1839554 bytes (1.8 MB)
==================================================
downloaded 1.8 MB
Warning:  we couldn't map to STRING 98% of your identifiers

Overwiev Volcano Phosho

Zoomed in Volcano plot Phospho


Proteome volcano plot

# Defining function first
volcano_plot_proteome <- function(proteome){
proteome$log2_FC <- log2(proteome$Abundance.Ratio...LIF.....Control.)
proteome$diffexpressed <- "NO"
proteome$diffexpressed[proteome$log2_FC > 1 & proteome$Abundance.Ratio.P.Value...LIF.....Control. < 0.05] <- "UP"
proteome$diffexpressed[proteome$log2_FC < -1 & proteome$Abundance.Ratio.P.Value...LIF.....Control. < 0.05] <- "DOWN"
ggplot(data=proteome, aes(x=log2_FC, y=-log10(Abundance.Ratio.P.Value...LIF.....Control.), col=diffexpressed))+
  geom_point()+
  theme_minimal()+
  geom_vline(xintercept=c(-1, 1), col="red") +
  geom_hline(yintercept=-log10(0.05), col="red")
}
#Printing the volcano plot
volcano_plot_proteome(raw_data_proteom) # Plots all the data 
Warning: Removed 852 rows containing missing values (geom_point).


Subsetting data_phospho for ratio values >= 2 and p-values <= 0.05

##Filter NA out in proteome, Filter out p.values that are non significant(not adjusted)
  #securing no overwrite to original data
df_phospho = data_phospho
df_proteome = raw_data_proteom
  
## Filter PHOSPHO
  more_phospho <- subset.data.frame(df_phospho, subset = df_phospho$Abudance.Ratio >= 2) #Filters out all ratios below 2-fold
  more_phospho <- subset.data.frame(more_phospho, subset = more_phospho$Abudance.pvalue <= 0.05) #Filters out all non-significant p-values
  
  ##Filter PROTEOME
  exclude_proteome <- filter(df_proteome, df_proteome$Abundance.Ratio.P.Value...LIF.....Control. <= 0.05 & df_proteome$Abundance.Ratio...LIF.....Control. >= 1) #Filters out all significant p-values, because significant p-values indicate an 
  #Data that we want to exclude, testing if the right data is selected with the volcano plot
  volcano_plot_proteome(exclude_proteome)

  #Anti_join anti_join() return all rows from x without a match in y. 
  more_proteome <- anti_join(df_proteome, exclude_proteome, by = c("Accession" = "Accession"))
  more_proteome <- filter(more_proteome, more_proteome$Abundance.Ratio...LIF.....Control. <= 2) # removes outliers with ratio bigger then 2
  #Checking the if the right data is excluded
  volcano_plot_proteome(more_proteome)
  
  #Inner join works too, but semi join is better becasue the proteome columns are not included into the resulting data frame.
  #more_phosphorylated <- dplyr::inner_join( more_phospho, more_proteome, by = c("Master.Protein.Accessions" = "Accession"))
  
  #Semi-join #semi_join() return all rows from x with a match in y. 
  more_phosphorylated <- dplyr::semi_join(more_phospho, more_proteome, by = c("Master.Protein.Accessions" = "Accession"))
  volcano_phospho_plot(more_phosphorylated, 2) # to control remaining data

  
remove(df_phospho, df_proteome, more_phospho, more_proteome, exclude_proteome)

Comparing data before and after the cleanup

-we could identify that the cleanup was conducted and not too stringent returning enough data to conduct analyisis on.


Volcano of data_phospho before filterZoomed in Volcano from more phosphorylated after filter


Building network map of the proteins

##Lower score_threshold, allowing more connections
string_db_low <- STRINGdb$new(version="11.5", species=10090, score_threshold=1, input_directory="") #species = mouse identifier, score threshold = if one interaction this will be mapped, no input directory data stored only temporary
hits_low <- string_db_low$map(more_phosphorylated, "Master.Protein.Accessions", removeUnmappedRows = F) #mapping the UNIPROT Ids from the first column to get STRING identifiers
string_db_low$plot_network(hits_low$STRING_id) #plotting network

invisible(string_db_low$get_png(hits_low$STRING_id, file = "~/phosphoproteomics_project/results/Network_low.png")) #Saving photo
## Higher score_threshold, allowing less connections
string_db_high <- STRINGdb$new(version="11.5", species=10090, score_threshold=200, input_directory="") #species = mouse identifier, score threshold = if one interaction this will be mapped, no input directory data stored only temporary
hits_high <- string_db_high$map(more_phosphorylated, "Master.Protein.Accessions", removeUnmappedRows = F) #mapping the UNIPROT Ids from the first column to get STRING identifiers
string_db_high$plot_network(hits_high$STRING_id) #plotting network

invisible(string_db_high$get_png(hits_high$STRING_id, file = "~/phosphoproteomics_project/results/Network_high.png")) #Saving photo

Way to identify the Proteins Stat5b is connected to.

#Finding String id identifier for Stat5b and assigning it to the a variable
i <- grep("P42232", hits_low$Master.Protein.Accessions) 
Stat5b <- string_db_low$mp("P42232") #mapping the UNIPROT Ids 
## Mapping Stat5b interactions in the less stringent data set and more stringent data set
#Defining a function to get 1. Proteins_connected_to_Stat5b, 2. Network plot, 3. Link to webpage, 4. Pathways
subnetworks <- function(data_h,score_h) {
  #a = The data we want to have a network from, b = the threshold number of the string_db function
  string_db <- STRINGdb$new(version="11.5", species=10090, score_threshold = score_h, input_directory="") #species = mouse 
  get <- string_db$get_subnetwork(data_h) #builds subnetwork from every entry in STRING_id
  ghi <- get[[Stat5b]][1] ##gives the list entry of "get" that lists all 18 entries that interact with Stat5b
  hg <- unique(ghi$`10090.ENSMUSP00000102981`) #gives the unique interaction (9 out of 18), 
  mat <- as_ids(ghi$`10090.ENSMUSP00000102981`) #function as_ids from the igraph package reads the class("igraph.vs")   and collapses it into a matrix
  Proteins_connected_to_Stat5b <- as.data.frame(unique(mat))
  names(Proteins_connected_to_Stat5b)[1] <- "STRING_id" #Renaming column so that add_protein_description recognizes it
  Proteins_connected_to_Stat5b[(nrow(Proteins_connected_to_Stat5b)+1),] <- Stat5b #Adds Stat5b to map its connections
  Proteins_connected_to_Stat5b <- string_db$add_proteins_description(Proteins_connected_to_Stat5b) #Add´s protein information to STRINGid´s
  
  #Plotting the subnetwork
  string_db$plot_network(Proteins_connected_to_Stat5b$STRING_id)
  
  #providing my link
  link <- string_db$get_link(Proteins_connected_to_Stat5b$STRING_id) #Provides link to the STRINGdb page
  
  #Providing the pathways
  pathways <- string_db$get_enrichment(Proteins_connected_to_Stat5b, category = "KEGG")
  return(list(Proteins_connected_to_Stat5b, pathways, link))
  
  remove(get,ghi,hg,mat)
   }
## Less stringent parameter to determine the Proteins that interact with Stat5
subnetwork_less_stringent <- subnetworks(data_h = hits_low$STRING_id, score_h = 1) #Using function
trying URL 'https://stringdb-static.org/download/protein.links.v11.5/10090.protein.links.v11.5.txt.gz'
Content type 'application/octet-stream' length 84569998 bytes (80.7 MB)
==================================================
downloaded 80.7 MB

#Sub-setting the returned list into original dataframes + link
Proteins_connected_to_Stat5b_less <- subnetwork_less_stringent[[1]]
pathways_less <- subnetwork_less_stringent[[2]]
Link_less <- subnetwork_less_stringent[[3]]
Link_less #Link to website
[1] "https://version-11-5.string-db.org/cgi/link?to=2E8CDC990DBD10F7"
invisible(string_db_low$get_png(Proteins_connected_to_Stat5b_less$STRING_id, file = "~/phosphoproteomics_project/results/Network_Stat5_less.png")) #Saving png
## More stringent parameter to determine the Proteins that interact with Stat5
subnetwork_more_stringent <- subnetworks(data_h = hits_high$STRING_id, score_h = 200) #Using function

#Sub-setting the returned list into original dataframes + link
Proteins_connected_to_Stat5b_more <- subnetwork_more_stringent[[1]] 
pathways_more <- subnetwork_more_stringent[[2]]
Link_more <- subnetwork_more_stringent[[3]]
Link_more #Link to website 
[1] "https://version-11-5.string-db.org/cgi/link?to=18B5C3E16E9B3AE0"
invisible(string_db_high$get_png(Proteins_connected_to_Stat5b_more$STRING_id, file = "~/phosphoproteomics_project/results/Network_Stat5_more.png")) #Saving png

Printing Pathways

-printing the enriched pathways from the subnetworks of Stat5b and renaming them for a better human readability & formatting of html document.

#Identifying only for Proteins connected to Stat5b
#Function to generate tabels based on factor gene counts / number of annotated genes.
factor_function <- function(Pathway){
i = 1
for (i in i:nrow(Pathway)) {
  Pathway$factor[i] <- round((Pathway[i,3]/Pathway[i,4]), digits = 3) #rounded for better visualization
}
Pathway <- rename(Pathway, background = number_of_genes_in_background)
Pathway <- rename(Pathway, hits = number_of_genes)
Pathway <- rename(Pathway, involved_genes = preferredNames)
Pathway <- Pathway[order(Pathway$factor,decreasing = T ),]
return(print.data.frame(Pathway[1:5,c(11,3,4,10,7)], row.names = F))
}
#Table for less stringent parameters
factor_function(pathways_less)
 factor hits background                 description            involved_genes
  0.054    4         74    Chronic myeloid leukemia Stat5a,Sos1,Ptpn11,Stat5b
  0.045    3         66  Non-small cell lung cancer        Stat5a,Sos1,Stat5b
  0.043    3         70      Acute myeloid leukemia        Stat5a,Sos1,Stat5b
  0.041    3         74 Prolactin signaling pathway        Stat5a,Sos1,Stat5b
  0.037    3         81      ErbB signaling pathway        Stat5a,Sos1,Stat5b
#Table for more stringent parameters
factor_function(pathways_more)
 factor hits background                                          description       involved_genes
  0.041    3         74                             Chronic myeloid leukemia Stat5a,Ptpn11,Stat5b
  0.030    3        101 AGE-RAGE signaling pathway in diabetic complications  Stat5a,Foxo1,Stat5b
  0.018    3        165                           JAK-STAT signaling pathway Stat5a,Ptpn11,Stat5b
     NA   NA         NA                                                 <NA>                 <NA>
     NA   NA         NA                                                 <NA>                 <NA>
## This part does not work with STRINGdb version 11.5, it worked properly with version 11.0b
#Identifying pathways also for Proteins with a ratio >=2 and p.value <= 0.05 and threshold = 200.
#Big_network_pathways_more <- string_db_high$get_enrichment(hits_high$STRING_id, category = "KEGG")
#factor_function(Big_network_pathways_more)
#Identifying pathways also for Proteins with a ratio >=2 and p.value <= 0.05 and threshold = 1.
#Big_network_pathways_less <- string_db_low$get_enrichment(hits_low$STRING_id, category = "KEGG")
#factor_function(Big_network_pathways_less)
#remove(i, Link_less, Link_more, string_db_high, string_db_low, subnetworks, volcano_plot_proteome, subnetwork_less_stringent, subnetwork_more_stringent, hits_high, hits_low, data_phospho, factor_function, volcano_phospho_plot, string_db_volcano)

Results and Discussion

Conclusion

Reproducibility



LS0tCnRpdGxlOiAiUGhvc3Bob3Byb3Rlb21pY3MiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBJbnRyb2R1Y3Rpb24KCi0gICBUaGUgcmVzZWFyY2ggcXVlc3Rpb24gY2FuIGJlIHN1bW1hcml6ZWQgYXM6ICJEaXNlbnRhbmdsZSBtZWNoYW5pc21zIG9mIGNlbGx1bGFyIGRpZmZlcmVudGlhdGlvbiBiZXR3ZWVuIHJlZ3VsYXIgc3RlbSBjZWxscyBhbmQgcGx1cmlwb3RlbnQgc3RlbSBjZWxscyBpbiBtaWNlIgotICAgVGhlIGFwcHJvYWNoIHVzZWQgaXMgcHJvdGVvbWljcyBiYXNlZCBvbiBNUyBkYXRhIHRoYXQgd2FzIGdlbmVyYXRlZCBpbiB0d28gZGlmZmVyZW50IGJhdGNoZXMuIFRoZSBkYXRhc2V0cyBhcmUgZ2VuZXJhdGVkIGJ5IGluZHVjaW5nIGRpZmZlcmVudGlhdGlvbiB3aXRoIGhvcnNlLXNlcnVtIG9uIG5vbi1kaWZmZXJlbnRpYXRlZCBtdXNjbGUgbW91c2UgY2VsbHMgKEMyQzEyIGNlbGxzKSBhcyBjb250cm9sIGFuZCBibG9ja2luZyB0aGUgZGlmZmVyZW50aWF0aW9uIGJ5IExJRiBpbiB0cmVhdGVkIHNhbXBsZS4gUHJvdGVvbWUgZGF0YSBzZXQgY29udGFpbnMgdGhyZWUgY29udHJvbCBzYW1wbGVzIGFuZCB0aHJlZSBMSUYgdHJlYXRlZCBzYW1wbGVzIHBlciBpZGVudGlmaWVkIHByb3RlaW4uIFRoZSBQaG9zcGhvIGRhdGEgc2V0IGNvbnRhaW5zIHRoZSBzYW1lIGFtb3VudCBvZiBzYW1wbGVzIGZyb20gYm90aCBncm91cHMsIGJ1dCB0aGVzZSB3ZXJlIGVucmljaGVkIGZvciBwaG9zcGhvcnlsYXRpb24uIFRoaXMgaXMgYSBmb2xsb3cgdXAgZXhwZXJpbWVudCwgc2luY2UgdGhlIGZpcnN0IGV4cGVyaW1lbnQgY29udGFpbmluZyBvbmx5IHRoZSBQcm90ZW9tZSBkYXRhIHNldCBzaG93ZWQgbm8gc2lnbmlmaWNhbnQgaW5jcmVhc2UgZnJvbSBhbnkgcHJvdGVpbiBhbmQgdGhlIG5ldyBoeXBvdGhlc2lzIGlzIGJhc2VkIG9uIHBvc3QtdHJhbnNsYXRpb25hbCBwaG9zcGhvcnlsYXRpb24gYXMgc2lnbmFsLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIERvd25sb2FkIHJhdyBkYXRhIGZyb20gVXBwbWF4CgpgYGB7YmFzaH0KI3JzeW5jIC1yIG5vYWhlQHJhY2toYW0udXBwbWF4LnV1LnNlOi9wcm9qL2cyMDIwMDA0L3ByaXZhdGUvc3R1ZGVudF9wcm9qZWN0cy9waG9zcGhvcHJvdGVvbWljcyAvaG9tZS9ub2FoL3Bob3NwaG9wcm90ZW9taWNzX3Byb2plY3QvCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIFNldHRpbmcgd29ya2luZyBkaXJlY3RvcnkgYW5kIGluc3RhbGwgbGlicmFyaWVzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30Kc2V0d2QoIn4vcGhvc3Bob3Byb3Rlb21pY3NfcHJvamVjdCIpClJlcXVpcmVkUGFja2FnZXMgPC0gYygiQmlvY01hbmFnZXIiLCAiZHBseXIiLCAiaWdyYXBoIiwgInRpZHlyIiwiZ2dwbG90MiIsImdncmVwZWwiKSAKI0luc3RhbGxzIHBhY2thZ2VzIGlmIG5vdCB5ZXQgaW5zdGFsbGVkCmZvciAoaSBpbiBSZXF1aXJlZFBhY2thZ2VzKSB7IAogICAgaWYgKCFyZXF1aXJlKGksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIAogICAgaW5zdGFsbC5wYWNrYWdlcyhpKQp9CkJpb2NNYW5hZ2VyOjppbnN0YWxsKHZlcnNpb24gPSAiMy4xNSIpCmxpYnJhcnkoQmlvY01hbmFnZXIpCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJTVFJJTkdkYiIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KFNUUklOR2RiKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyZXBlbCkKcmVtb3ZlKFJlcXVpcmVkUGFja2FnZXMpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIExvYWQgcmF3IGRhdGEKCi0gICBDaGFuZ2UgZGVmYXVsdCBzZXBhcmF0b3IgdG8gIjsiCgpgYGB7cn0KcmF3X2RhdGFfcGhvc3BobyA8LSByZWFkLmNzdihmaWxlID0gIn4vcGhvc3Bob3Byb3Rlb21pY3NfcHJvamVjdC9waG9zcGhvcHJvdGVvbWljcy9EYXRhc2V0X1Bob3NwaG8uY3N2Iiwgc2VwID0gIjsiKQpyYXdfZGF0YV9wcm90ZW9tIDwtIHJlYWQuY3N2KGZpbGUgPSAifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Bob3NwaG9wcm90ZW9taWNzL0RhdGFzZXRfUHJvdGVvbWUuY3N2Iiwgc2VwID0gIjsiKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBJZiBsb29wIHRvIG9taXQgbmEudmFsdWUgZnJvbSBhbGwgQWJ1bmRhbmNlIGNvbHVtbnMKCi0gICBncmVwIGxpbmUgc2VsZWN0cyBhbGwgY29sdW1ucyB0aGF0IGNvbnRhaW4gdGhlIHN0cmluZyAiQWJ1bmRhbmNlIiBhbmQgc2F2ZXMgdGhlIGNvbHVtbiBudW1iZXIgaW4gKkFidW5kYW5jZV9jb2x1bW5zKgotICAgVGhlIGZvciBsb29wIHRoZW4gc2VsZWN0cyBhbGwgcm93IHdlcmUgdGhlcmUgaXMgKm5vKiBOQSB2YWx1ZSBpbiBvbmUgb2YgdGhlIEFidW5kYW5jZSBjb2x1bW5zIGFuZCBjb3BpZXMgdGhlbSB0byBkYXRhX3Bob3NwaG8gdG8gbGVhdmUgdGhlIHJhdyBkYXRhIHVudG91Y2hlZC4KLSAgICoqIShpcy5uYShyYXdfZGF0YV9waG9zcGhvWyxpXSkpKiogZ2l2ZXMgb3V0IGFsbCB0aGUgcm93cyB0aGF0IGRvbid0IGhhdmUgYW4gTkEgdmFsdWUsIHRoZXNlIGdldCBzZWxlY3RlZAoKYGBge3J9CkFidW5kYW5jZV9jb2x1bW5zIDwtIGdyZXAoIkFidW5kYW5jZSIsIGNvbG5hbWVzKHJhd19kYXRhX3Bob3NwaG8pICkKZm9yIChpIGluIEFidW5kYW5jZV9jb2x1bW5zKSB7CiAgZGF0YV9waG9zcGhvIDwtIHJhd19kYXRhX3Bob3NwaG9bIShpcy5uYShyYXdfZGF0YV9waG9zcGhvWyxpXSkpLCBdCiAgfQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBDYWxjdWxhdGUgTWVhbiBmb3IgQWJ1bmRhbmNlIGFuZCBhZGRpbmcgaXQgYXMgY29sdW1uIGFuZCBSYXRpbyBMSUYvQ29udHJvbAoKLSAgIGdyZXAgYWdhaW4gQWJ1bmRhbmNlIGJ1dCB0aGlzIHRpbWUgdGhlIENvbnRyb2wgYW5kIExJRiAoY2FzZSkgc2VwYXJhdGVseS4KLSAgIGNhbGN1bGF0ZSByb3cgbWVhbnMgZWFjaCBhbmQgdGhlIHJhdGlvbiBiZXR3ZWVuIHRoZSBtZWFucy4KCmBgYHtyfQpBYnVuZGFuY2VfY29sdW1ucyA8LSBncmVwKCJBYnVuZGFuY2UuQ29udHJvbCIsIGNvbG5hbWVzKGRhdGFfcGhvc3BobykpCmRhdGFfcGhvc3BobyRBYnVkYW5jZS5NZWFuLkNvbnRyb2wgPC0gcm93TWVhbnMoZGF0YV9waG9zcGhvWyxjKEFidW5kYW5jZV9jb2x1bW5zKV0pCkFidW5kYW5jZV9jb2x1bW5zIDwtIGdyZXAoIkFidW5kYW5jZS5MSUYiLCBjb2xuYW1lcyhkYXRhX3Bob3NwaG8pKQpkYXRhX3Bob3NwaG8kQWJ1ZGFuY2UuTWVhbi5MSUYgPC0gcm93TWVhbnMoZGF0YV9waG9zcGhvWyxjKEFidW5kYW5jZV9jb2x1bW5zKV0pCmRhdGFfcGhvc3BobyRBYnVkYW5jZS5SYXRpbyA8LSBkYXRhX3Bob3NwaG8kQWJ1ZGFuY2UuTWVhbi5MSUYgLyBkYXRhX3Bob3NwaG8kQWJ1ZGFuY2UuTWVhbi5Db250cm9sCnJlbW92ZShBYnVuZGFuY2VfY29sdW1ucywgaSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgQ2FsY3VsYXRlIHAtdmFsdWVzCgotICAgKnQudGVzdCogdG8gY29tcGFyZSBhbGwgMyBDb250cm9sIFw8LVw+IDMgQ2FzZSB2YWx1ZXMuIElmIHRoZXkgYXJlIGRpZmZlcmVudCB0aGVuIHRoZSBwdmFsdWUgXDwgMC4wNSAqKi1cPioqIGluZGljYXRpbmcgYSByZWFsIGRpZmZlcmVuY2VzIGJldHdlZW4gY2FzZSAmIGNvbnRyb2wuIElmIHAtdmFsdWUgaXMgYmlnIHRoZSBkaWZmZXJlbmNlIGlzIGJ5IGNoYW5jZfCfmIQgSSB0cmllZCBpdCBmb3Igcm93IDE4OCB3aGljaCBoYXMgYSBSYXRpb24gbmVhcmx5IGVxdWFsIHRvIDEgKDEuMDIuLi4pIGluZGljYXRpbmcgbm8gZGlmZmVyZW5jZSBpbiBjYXNlIHZzLiBjb250cm9sIGFuZCB0aGUgcCB2YWx1ZSBcfiAwLjcgKHJlYWxseSBiYWQpIGFsc28gaW5kaWNhdGluZyBubyBkaWZmZXJlbmNlLiBUaGUgZm9yIGxvb3AgaXRlcmF0ZXMgaXQgb3ZlciBhbGwgcm93cywgQWxpY2VzIHF1aWNrZXIgYXBwcm9hY2ggdXNlcyB0aGUgYXBwbHkgZnVuY3Rpb24gdGhhdCBhbGxvd3MgcGFyYWxsZWwgaXRlcmF0aW9uIG1ha2luZyBpdCBxdWlja2VyLgoKYGBge3Igd2FybmluZz1GQUxTRX0Kc3RhcnRfZm9yIDwtIFN5cy50aW1lKCkKZm9yIChyb3cgaW4gMTpucm93KGRhdGFfcGhvc3BobykpIHsKICBwX3ZhbHVlIDwtIHQudGVzdChkYXRhX3Bob3NwaG9bcm93LDEwOjEyXSwgZGF0YV9waG9zcGhvW3JvdywxMzoxNV0sIGFsdGVybmF0aXZlID0gImxlc3MiKSAKICAjdGVzdHMgaWYgbWVhbiBvZiBjb250cm9sIGlzIGRpZmZlcmVudCBmcm9tIG1lYW4gb2YgTElGIChjYXNlKSBhbmQgYXNzaWducyBpdCB0byBsaXN0IHBfdmFsdWUKICBkYXRhX3Bob3NwaG8kQWJ1ZGFuY2UucHZhbHVlW3Jvd10gPC0gcF92YWx1ZSRwLnZhbHVlIAogICNzZWxlY3RzIHAudmFsdWUgZnJvbSBsaXN0IGFuZCBhc3NpZ25zIGl0IHRvIG5ldyBjb2x1bW4iQWJ1ZGFuY2UucHZhbHVlIiBpbiB0aGUgY29ycmVjdCByb3cKICB9CmVuZF9mb3IgPC0gU3lzLnRpbWUoKQplbmRfZm9yIC0gc3RhcnRfZm9yICNXZSB3YW50ZWQgdG8gY29tcGFyZSBjYWxjdWxhdGlvbiB0aW1lIHRvIGNvbXBhcmUgdGhlIG1ldGhvZHMKI0FsaWNlIHF1aWNrZXIgYXBwcm9hY2gKZGZfY29weSA8LSBkcGx5cjo6dGliYmxlKGRhdGFfcGhvc3BobykgIyBDb3B5IGRmCnN0YXJ0X2FsaWNlIDwtIFN5cy50aW1lKCkKZGZfY29weVtdIDwtIGxhcHBseShkZl9jb3B5LCBmdW5jdGlvbih4KSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcih4KSkpCmRmX2NvcHkkcFZhbHVlcyA8LSBhcHBseShkZl9jb3B5LCAxLCBmdW5jdGlvbih4KSB0LnRlc3QoeFsxMDoxMl0seFsxMzoxNV0sIGFsdGVybmF0aXZlID0gImxlc3MiLCBwYWlyZWQgPSBGKSRwLnZhbHVlKQplbmRfYWxpY2UgPC0gU3lzLnRpbWUoKQplbmRfYWxpY2Utc3RhcnRfYWxpY2UgI1Byb3ZlcyB0aGF0IGFwcGx5IGlzIHg0IGZhc3RlciB0aGVuIGxvb3AKcmVtb3ZlKGVuZF9hbGljZSxlbmRfZm9yLHN0YXJ0X2FsaWNlLHN0YXJ0X2Zvcixyb3cscF92YWx1ZSwgZGZfY29weSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgQWRkaW5nIGEgVm9sY2FubyBwbG90IHRvIGV4cGxvcmUgdGhlIGluaXRpYWwgZGF0YQoKLSAgIFRoZSBtYWluIGlkZWEgaXMgdG8gaWRlbnRpZnkgdGhyZXNob2xkcyB0aGF0IGNhbiBiZSB1c2VkIHRvIGNsZWFuIHRoZSByYXcgZGF0YS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIyBQaG9zcGhvIHZvbGNhbm8gcGxvdApzdHJpbmdfZGJfdm9sY2FubyA8LSBTVFJJTkdkYiRuZXcoIHZlcnNpb249IjExLjUiLCBzcGVjaWVzPTEwMDkwLCBzY29yZV90aHJlc2hvbGQ9MSwgaW5wdXRfZGlyZWN0b3J5PSIiKQojIFRoaXMgaGFkIHRvIGJlIG91dHNpZGUgdGhlIHZvbGNhbm9fcGhvc3Bob19wbG90IGZ1bmN0aW9uLCBiZWNhdXNlIHRoZSBmdW5jdGlvbiBkaWRudCByZXR1cm4gdGhlIHN0cmluZ19kYl92b2xjYW5vIGZ1bmN0aW9uIHRoYXQgaXMgbmVlZGVkIGFsc28gYWZ0ZXJ3YXJkcy4KIyBEZWZpbmluZyBmdW5jdGlvbiB0byBwbG90IGRhdGEKICAgICMgTWFrZXMgdGhlIHdob2xlIGNvZGUgZWFzeSB0byByZXVzZS4Kdm9sY2Fub19waG9zcGhvX3Bsb3QgPC0gZnVuY3Rpb24ocGhvc3BobywgbnVtYmVyKXsKIyBDYWxjdWxhdGUgTElGL0NvbnRyb2wgcmF0aW8KcGhvc3BobyRGQyA8LSBwaG9zcGhvJEFidWRhbmNlLk1lYW4uTElGIC8gcGhvc3BobyRBYnVkYW5jZS5NZWFuLkNvbnRyb2wKcGhvc3BobyRsb2cyX0ZDIDwtIGxvZzIocGhvc3BobyRGQykKcGhvc3BobyRkaWZmZXhwcmVzc2VkIDwtICJOTyIKcGhvc3BobyRkaWZmZXhwcmVzc2VkW3Bob3NwaG8kbG9nMl9GQyA+IDEgJiBwaG9zcGhvJEFidWRhbmNlLnB2YWx1ZSA8IDAuMDVdIDwtICJVUCIgIyBzb3J0cyBvdXQgdXByZWd1bGF0ZWQgcGhvc3BvIHZhbHVlcyBhbmQgYXNzaWducyAiVVAiIHZhbHVlLgpwaG9zcGhvJGRpZmZleHByZXNzZWRbcGhvc3BobyRsb2cyX0ZDIDwgLTEgJiBwaG9zcGhvJEFidWRhbmNlLnB2YWx1ZSA8IDAuMDVdIDwtICJET1dOIiAjIHNhbWUgb3IgZG93bnJlZ3VsYXRlZCBwaG9zcGhvIHZhbHVlcwpwaG9zcGhvJGRlbGFiZWwgPC0gTkEKcGhvc3BobyRkZWxhYmVsW3Bob3NwaG8kZGlmZmV4cHJlc3NlZCAhPSAiTk8iXSA8LSBwaG9zcGhvJE1hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnNbcGhvc3BobyRkaWZmZXhwcmVzc2VkICE9ICJOTyJdICMgc2VsZWN0aW5nIG9ubHkgdXAgb3IgZG93biByZWd1bGF0ZWQgdmFsdWVzIGFuZCBhc3NpZ25pbmcgdGhlbSB0byB0aGUgbGFiZWwgY29sdW1uLgojI0FkZGl0aW9uIHRvIGdlbmVyYXRlICJyZWFsIG5hbWVzIiBub3QgVW5pcHJvdCBJRHMKTWFwcGluZ19waG9zcGhvIDwtIHN0cmluZ19kYl92b2xjYW5vJG1hcChwaG9zcGhvLCAiZGVsYWJlbCIsIHJlbW92ZVVubWFwcGVkUm93cyA9IEYpCk1hcHBpbmdfcGhvc3BobyA8LSBzdHJpbmdfZGJfdm9sY2FubyRhZGRfcHJvdGVpbnNfZGVzY3JpcHRpb24oTWFwcGluZ19waG9zcGhvKSAjQWRkwrRzIHByb3RlaW4gaW5mb3JtYXRpb24gdG8gU1RSSU5HaWTCtHMgPSBwcmVmZXJyZWQgbmFtZXMgYXJlIG1vcmUgaHVtYW4gcmVhZGFibGUKI1ZvbGNhbm8gcGxvdCBvdmVydmlldwpnZ3Bsb3QoZGF0YT1NYXBwaW5nX3Bob3NwaG8sIGFlcyh4PWxvZzJfRkMsIHk9LWxvZzEwKEFidWRhbmNlLnB2YWx1ZSksIGNvbD1kaWZmZXhwcmVzc2VkLCBsYWJlbD1wcmVmZXJyZWRfbmFtZSkpKwogIGdlb21fcG9pbnQoKSsKICB0aGVtZV9taW5pbWFsKCkrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoLTEsIDEpLCBjb2w9InJlZCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDUpLCBjb2w9InJlZCIpCiNzYXZlcyB0aGUgY29ycmVjdCBwaG90byBmb3IgYm90aCBwb3NzaWJsZSBpbnB1dCB0YWJsZXMKIyBudW1iZXIgMSBvciAyIGhhcyB0byBiZSBkZWZpbmVkIGluIHRoZSBmdW5jdGlvbiBpbnB1dCBkYXRhLiBUaGlzIGlzIGEgYml0IGNsdW1zeSBzb2x2ZWQsIGJ1dCB0aGVyZSB3ZXJlIHByb2JsZW1zIHdpdGggcmV0dXJuaW5nIG1vcmUgdGhhbiBvbmUgcGxvdCBvdXQgb2YgYSBmdW5jdGlvbi4KaWYgKG51bWJlciA9PSAxKSB7CiAgZ2dzYXZlKCJ+L3Bob3NwaG9wcm90ZW9taWNzX3Byb2plY3QvcmVzdWx0cy9ab29tZWRfb3V0X1ZvbGNhbm8ucG5nIiwgd2lkdGggPSAxNTAwLCBoZWlnaHQgPSAxMDAwLCB1bml0cyA9ICJweCIpICMgU2F2ZXMgcGxvdHMgd2l0aCBkZWZpbmVkIHNpemUKfQppZiAobnVtYmVyID09IDIpIHsKICBnZ3NhdmUoIn4vcGhvc3Bob3Byb3Rlb21pY3NfcHJvamVjdC9yZXN1bHRzL1pvb21lZF9vdXRfVm9sY2Fub19mb3JfbW9yZV9waG9zcGhyeWxhdGVkX2FmdGVyX3FjX2FuZF9maWx0ZXIucG5nIiwgd2lkdGggPSAxNTAwLCBoZWlnaHQgPSAxMDAwLCB1bml0cyA9ICJweCIpCn0KI0RldGFpbC9ab29tZWQgaW4KZ2dwbG90KGRhdGE9TWFwcGluZ19waG9zcGhvLCBhZXMoeD1sb2cyX0ZDLCB5PS1sb2cxMChBYnVkYW5jZS5wdmFsdWUpLCBjb2w9ZGlmZmV4cHJlc3NlZCwgbGFiZWw9cHJlZmVycmVkX25hbWUpKSsKICBnZW9tX3BvaW50KCkrCiAgdGhlbWVfbWluaW1hbCgpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKC0xLCAxKSwgY29sPSJyZWQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjA1KSwgY29sPSJyZWQiKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxLDMuNSksIHlsaW0gPSBjKDAuNSwgNikpKwogIGdlb21fbGFiZWxfcmVwZWwoKQojc2F2ZXMgdGhlIGNvcnJlY3QgcGhvdG8gZm9yIGJvdGggcG9zc2libGUgaW5wdXQgdGFibGVzCmlmIChudW1iZXIgPT0gMSkgewogIGdnc2F2ZSgifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvWm9vbWVkX2luX1ZvbGNhbm8ucG5nIiwgd2lkdGggPSAxNTAwLCBoZWlnaHQgPSAxMDAwLCB1bml0cyA9ICJweCIpCn0KaWYgKG51bWJlciA9PSAyKSB7Cmdnc2F2ZSgifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvWm9vbWVkX2luX1ZvbGNhbm9fZm9yX21vcmVfcGhvc3BocnlsYXRlZF9hZnRlcl9xY19hbmRfZmlsdGVyLnBuZyIsIHdpZHRoID0gMTUwMCwgaGVpZ2h0ID0gMTAwMCwgdW5pdHMgPSAicHgiKQp9Cn0Kdm9sY2Fub19waG9zcGhvX3Bsb3QoZGF0YV9waG9zcGhvLCAxKSAjcGxvdHMgZGF0YSB0byBjb250cm9sCmBgYAoKIVtPdmVyd2lldiBWb2xjYW5vIFBob3Nob10oaW1hZ2VzL1pvb21lZF9vdXRfVm9sY2Fuby5wbmcpCgohW1pvb21lZCBpbiBWb2xjYW5vIHBsb3QgUGhvc3Bob10oaW1hZ2VzL1pvb21lZF9pbl9Wb2xjYW5vLnBuZykKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBQcm90ZW9tZSB2b2xjYW5vIHBsb3QKCi0gICBBbHNvIHBsb3R0aW5nIHRoZSBwcm90ZW9tZSBkYXRhIHRvIGlkZW50aWZ5IHRocmVzaG9sZHMgdG8gY2xlYW4gZGF0YQoKYGBge3J9CiMgRGVmaW5pbmcgZnVuY3Rpb24gZmlyc3QKdm9sY2Fub19wbG90X3Byb3Rlb21lIDwtIGZ1bmN0aW9uKHByb3Rlb21lKXsKcHJvdGVvbWUkbG9nMl9GQyA8LSBsb2cyKHByb3Rlb21lJEFidW5kYW5jZS5SYXRpby4uLkxJRi4uLi4uQ29udHJvbC4pCnByb3Rlb21lJGRpZmZleHByZXNzZWQgPC0gIk5PIgpwcm90ZW9tZSRkaWZmZXhwcmVzc2VkW3Byb3Rlb21lJGxvZzJfRkMgPiAxICYgcHJvdGVvbWUkQWJ1bmRhbmNlLlJhdGlvLlAuVmFsdWUuLi5MSUYuLi4uLkNvbnRyb2wuIDwgMC4wNV0gPC0gIlVQIgpwcm90ZW9tZSRkaWZmZXhwcmVzc2VkW3Byb3Rlb21lJGxvZzJfRkMgPCAtMSAmIHByb3Rlb21lJEFidW5kYW5jZS5SYXRpby5QLlZhbHVlLi4uTElGLi4uLi5Db250cm9sLiA8IDAuMDVdIDwtICJET1dOIgpnZ3Bsb3QoZGF0YT1wcm90ZW9tZSwgYWVzKHg9bG9nMl9GQywgeT0tbG9nMTAoQWJ1bmRhbmNlLlJhdGlvLlAuVmFsdWUuLi5MSUYuLi4uLkNvbnRyb2wuKSwgY29sPWRpZmZleHByZXNzZWQpKSsKICBnZW9tX3BvaW50KCkrCiAgdGhlbWVfbWluaW1hbCgpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKC0xLCAxKSwgY29sPSJyZWQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjA1KSwgY29sPSJyZWQiKQp9CiNQcmludGluZyB0aGUgdm9sY2FubyBwbG90CnZvbGNhbm9fcGxvdF9wcm90ZW9tZShyYXdfZGF0YV9wcm90ZW9tKSAjIFBsb3RzIGFsbCB0aGUgZGF0YSAKYGBgCgotICAgTm8gdmFsdWVzIGFyZSBzaWduaWZpY2FudGx5IHVwIG9yIGRvd24gcmVndWxhdGVkIChkZWZpbmluZyB1cCAvIGRvd24gcmVndWxhdGVkIGFzIGF0IGxlYXN0IDItZm9sZCBpbmNyZWFzZS9kZWNyZWFzZSkuIEFsc28gb3V0bGllcnMgY291bGQgYmUgb2JzZXJ2ZWQgKDQgdmFsZXMgd2l0aCBhIFw+Mi1mb2xkIGluY3JlYXNlIHdpdGggbm9uLXNpZ25pZmljYW50IHAtdmFsdWVzKS4gVG8gYmUgbW9yZSByZXN0cmljdGl2ZSBhbmQgZGVjcmVhc2UgdGhlIGNoYW5nZSBvZiBmYWxzZSBwb3NpdGl2ZXMgaW4gdGhlIHVwY29taW5nIGFuYWx5c2lzLCBhbHNvIHNpZ25pZmljYW50bHkgaW5jcmVhc2VkIHZhbHVlcyB3aXRoIGEgcmF0aW8gXD4gMSB3ZXJlIHNlbGVjdGVkIGFzIHVuZGVzaXJlZCBkYXRhIGFuZCBhcmUgZ29pbmcgdG8gYmUgcmVtb3ZlZCBpbiB0aGUgZm9sbG93aW5nIGNsZWFuaW5nIHN0ZXBzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIFN1YnNldHRpbmcgZGF0YV9waG9zcGhvIGZvciByYXRpbyB2YWx1ZXMgXD49IDIgYW5kIHAtdmFsdWVzIFw8PSAwLjA1CgotICAgCgogICAgYS4gIFJlbW92ZSBwZXB0aWRlcyB3aXRob3V0IHF1YW50aXRhdGl2ZSB2YWx1ZXMgLVw+IEFscmVhZHkgZG9uZSBpbiAiSWYgbG9vcCB0byBvbWl0IG5hLnZhbHVlIGZyb20gYWxsIEFidW5kYW5jZSBjb2x1bW5zIiB0aGVyZWJ5IGNyZWF0aW5nIHRoZSBkYXRhX3Bob3NwaG8KCi0gICBIZXJlIHdlIHVzZSB0aGUgaWRlbnRpZmllZCB0aHJlc2hvbGRzIGZyb20gdGhlIHZvbGNhbm8gcGxvdHMgdG8gY2xlYW4gdGhlIGRhdGEuIEZpcnN0IHdlIHNlbGVjdCByb3dzIHdpdGggcmF0aW8gXD49IDIgYW5kIHAudmFsdWVzIFw8PSAwLjA1IGZyb20gdGhlIHBob3BobyBkYXRhc2V0LiBGcm9tIHRoZSBwcm90ZW9tIGRhdGEgc2V0IHZhbHVlcyB3ZXJlIHNlbGVjdGVkIHdpdGggYSBhbGwgcm93cyB3aXRoIGEgcmF0aW8gXDw9IDEsIGFsbCByb3dzIHdpdGggYSByYXRpbyBcPj0gMSBhbmQgcC52YWx1ZSBcPD0gMC4wNSAoZWxldmF0ZWQgcHJvdGVpbnMgKHJhdGlvIFw+PTEpIHdpdGggc2lnbmlmaWNhbnQgcC12YWx1ZXMgYXJlIGV4Y2x1ZGVkIGFuZCBwcm90ZWluIHdpdGggcmF0aW9zIGJpZ2dlciB0aGFuIDIgKGRpc3JlZ2FyZGluZyBwLXZhbHVlKSwgdG9vKS4gQWZ0ZXIgdGhpcyBpbml0aWFsIGNsZWFudXAgYSBzZW1pX2pvaW4oKSB3YXMgcGVyZm9ybWVkIHJldHVybmluZyBhbGwgcm93cyBmcm9tIGNsZWFudXAgcGhvc3BobyB3aXRoIGEgbWF0Y2ggaW4gY2xlYW51cCBwcm90ZW9tZSwgdG8gc2VsZWN0IG9ubHkgcGhvc3BvIHZhbHVlcyB0aGF0IHNob3dlZCBpbmNyZWFzZWQgcGhvc3Bob3J5bGF0aW9uIGR1ZSB0byB0aGUgTElGIHRyZWF0bWVudCBhbmQgbm90IGR1ZSB0byBlbGV2YXRlZCBleHByZXNzaW9uIGxldmVscy4KCi0gICB2b2xjYW5vIHBsb3RzIHdlcmUgdXNlZCB0byB2aXN1YWxpemUgdGhlIGNsZWFuaW5nL3NlbGVjdGluZyBzdGVwcwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyNGaWx0ZXIgTkEgb3V0IGluIHByb3Rlb21lLCBGaWx0ZXIgb3V0IHAudmFsdWVzIHRoYXQgYXJlIG5vbiBzaWduaWZpY2FudChub3QgYWRqdXN0ZWQpCiAgI3NlY3VyaW5nIG5vIG92ZXJ3cml0ZSB0byBvcmlnaW5hbCBkYXRhCmRmX3Bob3NwaG8gPSBkYXRhX3Bob3NwaG8KZGZfcHJvdGVvbWUgPSByYXdfZGF0YV9wcm90ZW9tCiAgCiMjIEZpbHRlciBQSE9TUEhPCiAgbW9yZV9waG9zcGhvIDwtIHN1YnNldC5kYXRhLmZyYW1lKGRmX3Bob3NwaG8sIHN1YnNldCA9IGRmX3Bob3NwaG8kQWJ1ZGFuY2UuUmF0aW8gPj0gMikgI0ZpbHRlcnMgb3V0IGFsbCByYXRpb3MgYmVsb3cgMi1mb2xkCiAgbW9yZV9waG9zcGhvIDwtIHN1YnNldC5kYXRhLmZyYW1lKG1vcmVfcGhvc3Bobywgc3Vic2V0ID0gbW9yZV9waG9zcGhvJEFidWRhbmNlLnB2YWx1ZSA8PSAwLjA1KSAjRmlsdGVycyBvdXQgYWxsIG5vbi1zaWduaWZpY2FudCBwLXZhbHVlcwogIAogICMjRmlsdGVyIFBST1RFT01FCiAgZXhjbHVkZV9wcm90ZW9tZSA8LSBmaWx0ZXIoZGZfcHJvdGVvbWUsIGRmX3Byb3Rlb21lJEFidW5kYW5jZS5SYXRpby5QLlZhbHVlLi4uTElGLi4uLi5Db250cm9sLiA8PSAwLjA1ICYgZGZfcHJvdGVvbWUkQWJ1bmRhbmNlLlJhdGlvLi4uTElGLi4uLi5Db250cm9sLiA+PSAxKSAjRmlsdGVycyBvdXQgYWxsIHNpZ25pZmljYW50IHAtdmFsdWVzLCBiZWNhdXNlIHNpZ25pZmljYW50IHAtdmFsdWVzIGluZGljYXRlIGFuIAogICNEYXRhIHRoYXQgd2Ugd2FudCB0byBleGNsdWRlLCB0ZXN0aW5nIGlmIHRoZSByaWdodCBkYXRhIGlzIHNlbGVjdGVkIHdpdGggdGhlIHZvbGNhbm8gcGxvdAogIHZvbGNhbm9fcGxvdF9wcm90ZW9tZShleGNsdWRlX3Byb3Rlb21lKQogICNBbnRpX2pvaW4gYW50aV9qb2luKCkgcmV0dXJuIGFsbCByb3dzIGZyb20geCB3aXRob3V0IGEgbWF0Y2ggaW4geS4gCiAgbW9yZV9wcm90ZW9tZSA8LSBhbnRpX2pvaW4oZGZfcHJvdGVvbWUsIGV4Y2x1ZGVfcHJvdGVvbWUsIGJ5ID0gYygiQWNjZXNzaW9uIiA9ICJBY2Nlc3Npb24iKSkKICBtb3JlX3Byb3Rlb21lIDwtIGZpbHRlcihtb3JlX3Byb3Rlb21lLCBtb3JlX3Byb3Rlb21lJEFidW5kYW5jZS5SYXRpby4uLkxJRi4uLi4uQ29udHJvbC4gPD0gMikgIyByZW1vdmVzIG91dGxpZXJzIHdpdGggcmF0aW8gYmlnZ2VyIHRoZW4gMgogICNDaGVja2luZyB0aGUgaWYgdGhlIHJpZ2h0IGRhdGEgaXMgZXhjbHVkZWQKICB2b2xjYW5vX3Bsb3RfcHJvdGVvbWUobW9yZV9wcm90ZW9tZSkKICAKICAjSW5uZXIgam9pbiB3b3JrcyB0b28sIGJ1dCBzZW1pIGpvaW4gaXMgYmV0dGVyIGJlY2FzdWUgdGhlIHByb3Rlb21lIGNvbHVtbnMgYXJlIG5vdCBpbmNsdWRlZCBpbnRvIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZS4KICAjbW9yZV9waG9zcGhvcnlsYXRlZCA8LSBkcGx5cjo6aW5uZXJfam9pbiggbW9yZV9waG9zcGhvLCBtb3JlX3Byb3Rlb21lLCBieSA9IGMoIk1hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnMiID0gIkFjY2Vzc2lvbiIpKQogIAogICNTZW1pLWpvaW4gI3NlbWlfam9pbigpIHJldHVybiBhbGwgcm93cyBmcm9tIHggd2l0aCBhIG1hdGNoIGluIHkuIAogIG1vcmVfcGhvc3Bob3J5bGF0ZWQgPC0gZHBseXI6OnNlbWlfam9pbihtb3JlX3Bob3NwaG8sIG1vcmVfcHJvdGVvbWUsIGJ5ID0gYygiTWFzdGVyLlByb3RlaW4uQWNjZXNzaW9ucyIgPSAiQWNjZXNzaW9uIikpCiAgdm9sY2Fub19waG9zcGhvX3Bsb3QobW9yZV9waG9zcGhvcnlsYXRlZCwgMikgIyB0byBjb250cm9sIHJlbWFpbmluZyBkYXRhCiAgCnJlbW92ZShkZl9waG9zcGhvLCBkZl9wcm90ZW9tZSwgbW9yZV9waG9zcGhvLCBtb3JlX3Byb3Rlb21lLCBleGNsdWRlX3Byb3Rlb21lKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBDb21wYXJpbmcgZGF0YSBiZWZvcmUgYW5kIGFmdGVyIHRoZSBjbGVhbnVwCgotd2UgY291bGQgaWRlbnRpZnkgdGhhdCB0aGUgY2xlYW51cCB3YXMgY29uZHVjdGVkIGFuZCBub3QgdG9vIHN0cmluZ2VudCByZXR1cm5pbmcgZW5vdWdoIGRhdGEgdG8gY29uZHVjdCBhbmFseWlzaXMgb24uCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgIVtWb2xjYW5vIG9mIGRhdGFfcGhvc3BobyBiZWZvcmUgZmlsdGVyXShpbWFnZXMvWm9vbWVkX2luX1ZvbGNhbm8tMDEucG5nKXt3aWR0aD0iMzUwIn0hW1pvb21lZCBpbiBWb2xjYW5vIGZyb20gbW9yZSBwaG9zcGhvcnlsYXRlZCBhZnRlciBmaWx0ZXJdKGltYWdlcy9ab29tZWRfaW5fVm9sY2Fub19mb3JfbW9yZV9waG9zcGhyeWxhdGVkX2FmdGVyX3FjX2FuZF9maWx0ZXIucG5nKXt3aWR0aD0iMzUwIn0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyAKCiMjIyMgQnVpbGRpbmcgbmV0d29yayBtYXAgb2YgdGhlIHByb3RlaW5zCgotICAgV2l0aCBoZWxwIG9mIHRoZSBTVFJJTmdkYiBwYWNrYWdlIHRoZSBvdmVydmlldyBuZXR3b3JrcyB3ZXJlIHByb2R1Y2VkIHdpdGggZGlmZmVyZW50IHRocmVzaG9sZHMuCgpgYGB7cn0KIyNMb3dlciBzY29yZV90aHJlc2hvbGQsIGFsbG93aW5nIG1vcmUgY29ubmVjdGlvbnMKc3RyaW5nX2RiX2xvdyA8LSBTVFJJTkdkYiRuZXcodmVyc2lvbj0iMTEuNSIsIHNwZWNpZXM9MTAwOTAsIHNjb3JlX3RocmVzaG9sZD0xLCBpbnB1dF9kaXJlY3Rvcnk9IiIpICNzcGVjaWVzID0gbW91c2UgaWRlbnRpZmllciwgc2NvcmUgdGhyZXNob2xkID0gaWYgb25lIGludGVyYWN0aW9uIHRoaXMgd2lsbCBiZSBtYXBwZWQsIG5vIGlucHV0IGRpcmVjdG9yeSBkYXRhIHN0b3JlZCBvbmx5IHRlbXBvcmFyeQpoaXRzX2xvdyA8LSBzdHJpbmdfZGJfbG93JG1hcChtb3JlX3Bob3NwaG9yeWxhdGVkLCAiTWFzdGVyLlByb3RlaW4uQWNjZXNzaW9ucyIsIHJlbW92ZVVubWFwcGVkUm93cyA9IEYpICNtYXBwaW5nIHRoZSBVTklQUk9UIElkcyBmcm9tIHRoZSBmaXJzdCBjb2x1bW4gdG8gZ2V0IFNUUklORyBpZGVudGlmaWVycwpzdHJpbmdfZGJfbG93JHBsb3RfbmV0d29yayhoaXRzX2xvdyRTVFJJTkdfaWQpICNwbG90dGluZyBuZXR3b3JrCmludmlzaWJsZShzdHJpbmdfZGJfbG93JGdldF9wbmcoaGl0c19sb3ckU1RSSU5HX2lkLCBmaWxlID0gIn4vcGhvc3Bob3Byb3Rlb21pY3NfcHJvamVjdC9yZXN1bHRzL05ldHdvcmtfbG93LnBuZyIpKSAjU2F2aW5nIHBob3RvCiMjIEhpZ2hlciBzY29yZV90aHJlc2hvbGQsIGFsbG93aW5nIGxlc3MgY29ubmVjdGlvbnMKc3RyaW5nX2RiX2hpZ2ggPC0gU1RSSU5HZGIkbmV3KHZlcnNpb249IjExLjUiLCBzcGVjaWVzPTEwMDkwLCBzY29yZV90aHJlc2hvbGQ9MjAwLCBpbnB1dF9kaXJlY3Rvcnk9IiIpICNzcGVjaWVzID0gbW91c2UgaWRlbnRpZmllciwgc2NvcmUgdGhyZXNob2xkID0gaWYgb25lIGludGVyYWN0aW9uIHRoaXMgd2lsbCBiZSBtYXBwZWQsIG5vIGlucHV0IGRpcmVjdG9yeSBkYXRhIHN0b3JlZCBvbmx5IHRlbXBvcmFyeQpoaXRzX2hpZ2ggPC0gc3RyaW5nX2RiX2hpZ2gkbWFwKG1vcmVfcGhvc3Bob3J5bGF0ZWQsICJNYXN0ZXIuUHJvdGVpbi5BY2Nlc3Npb25zIiwgcmVtb3ZlVW5tYXBwZWRSb3dzID0gRikgI21hcHBpbmcgdGhlIFVOSVBST1QgSWRzIGZyb20gdGhlIGZpcnN0IGNvbHVtbiB0byBnZXQgU1RSSU5HIGlkZW50aWZpZXJzCnN0cmluZ19kYl9oaWdoJHBsb3RfbmV0d29yayhoaXRzX2hpZ2gkU1RSSU5HX2lkKSAjcGxvdHRpbmcgbmV0d29yawppbnZpc2libGUoc3RyaW5nX2RiX2hpZ2gkZ2V0X3BuZyhoaXRzX2hpZ2gkU1RSSU5HX2lkLCBmaWxlID0gIn4vcGhvc3Bob3Byb3Rlb21pY3NfcHJvamVjdC9yZXN1bHRzL05ldHdvcmtfaGlnaC5wbmciKSkgI1NhdmluZyBwaG90bwpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBXYXkgdG8gaWRlbnRpZnkgdGhlIFByb3RlaW5zIFN0YXQ1YiBpcyBjb25uZWN0ZWQgdG8uCgotICAgSW4gdGhpcyBjaHVuayB3ZSBkZWZpbmUgYSBmdW5jdGlvbiB3aXRoIGEgbW9yZSBhbmQgYSBsZXNzIHN0cmluZ2VudCB0aHJlc2hvbGQgdG8gZ2V0IDEuIFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIgZm9yIG1vcmUgb3IgbGVzcyBzdHJpbmdlbnQgdGhyZXNob2xkLCAyLiBOZXR3b3JrIHBsb3QgZm9yIHRoZSBzdWJuZXR3b3JrcyBvZiBwcm90ZWlucyBjb25uZWN0ZWQgdG8gU3RhdDViLCAzLiBMaW5rIHRvIHdlYnBhZ2UgZm9yIHRoZSBzdWJuZXR3b3JrcywgNC4gUGF0aHdheXMgdGhhdCBhcmUgZW5yaWNoZWQgaW4gdGhlIHN1Ym5ldHdvcmtzLgoKLSAgIFRoZXJlIGlzIGFsc28gYSBsaW5rIHRvIHRoZSBbU1RSSU5HZGIgd2Vic2l0ZSBmb3IgbGVzcyBzdHJpbmdlbnQgdGhyZXNob2xkID0gMV0oaHR0cHM6Ly92ZXJzaW9uLTExLTUuc3RyaW5nLWRiLm9yZy9jZ2kvbGluaz90bz0yRThDREM5OTBEQkQxMEY3KSB3aXRoIHRoZSBQcm90ZWlucyBjb25uZWN0ZWQgdG8gU3RhdDViIGluIHRoaXMgZGF0YXNldC4KCi0gICBUaGVyZSBpcyBhbHNvIGEgbGluayB0byB0aGUgW1NUUklOR2RiIHdlYnNpdGUgZm9yIG1vcmUgc3RyaW5nZW50IHRocmVzaG9sZCA9IDIwMF0oaHR0cHM6Ly92ZXJzaW9uLTExLTUuc3RyaW5nLWRiLm9yZy9jZ2kvbGluaz90bz0xOEI1QzNFMTZFOUIzQUUwKSB3aXRoIHRoZSBQcm90ZWlucyBjb25uZWN0ZWQgdG8gU3RhdDViIGluIHRoaXMgZGF0YXNldC4KCmBgYHtyfQojRmluZGluZyBTdHJpbmcgaWQgaWRlbnRpZmllciBmb3IgU3RhdDViIGFuZCBhc3NpZ25pbmcgaXQgdG8gdGhlIGEgdmFyaWFibGUKaSA8LSBncmVwKCJQNDIyMzIiLCBoaXRzX2xvdyRNYXN0ZXIuUHJvdGVpbi5BY2Nlc3Npb25zKSAKU3RhdDViIDwtIHN0cmluZ19kYl9sb3ckbXAoIlA0MjIzMiIpICNtYXBwaW5nIHRoZSBVTklQUk9UIElkcyAKIyMgTWFwcGluZyBTdGF0NWIgaW50ZXJhY3Rpb25zIGluIHRoZSBsZXNzIHN0cmluZ2VudCBkYXRhIHNldCBhbmQgbW9yZSBzdHJpbmdlbnQgZGF0YSBzZXQKI0RlZmluaW5nIGEgZnVuY3Rpb24gdG8gZ2V0IDEuIFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIsIDIuIE5ldHdvcmsgcGxvdCwgMy4gTGluayB0byB3ZWJwYWdlLCA0LiBQYXRod2F5cwpzdWJuZXR3b3JrcyA8LSBmdW5jdGlvbihkYXRhX2gsc2NvcmVfaCkgewogICNhID0gVGhlIGRhdGEgd2Ugd2FudCB0byBoYXZlIGEgbmV0d29yayBmcm9tLCBiID0gdGhlIHRocmVzaG9sZCBudW1iZXIgb2YgdGhlIHN0cmluZ19kYiBmdW5jdGlvbgogIHN0cmluZ19kYiA8LSBTVFJJTkdkYiRuZXcodmVyc2lvbj0iMTEuNSIsIHNwZWNpZXM9MTAwOTAsIHNjb3JlX3RocmVzaG9sZCA9IHNjb3JlX2gsIGlucHV0X2RpcmVjdG9yeT0iIikgI3NwZWNpZXMgPSBtb3VzZSAKICBnZXQgPC0gc3RyaW5nX2RiJGdldF9zdWJuZXR3b3JrKGRhdGFfaCkgI2J1aWxkcyBzdWJuZXR3b3JrIGZyb20gZXZlcnkgZW50cnkgaW4gU1RSSU5HX2lkCiAgZ2hpIDwtIGdldFtbU3RhdDViXV1bMV0gIyNnaXZlcyB0aGUgbGlzdCBlbnRyeSBvZiAiZ2V0IiB0aGF0IGxpc3RzIGFsbCAxOCBlbnRyaWVzIHRoYXQgaW50ZXJhY3Qgd2l0aCBTdGF0NWIKICBoZyA8LSB1bmlxdWUoZ2hpJGAxMDA5MC5FTlNNVVNQMDAwMDAxMDI5ODFgKSAjZ2l2ZXMgdGhlIHVuaXF1ZSBpbnRlcmFjdGlvbiAoOSBvdXQgb2YgMTgpLCAKICBtYXQgPC0gYXNfaWRzKGdoaSRgMTAwOTAuRU5TTVVTUDAwMDAwMTAyOTgxYCkgI2Z1bmN0aW9uIGFzX2lkcyBmcm9tIHRoZSBpZ3JhcGggcGFja2FnZSByZWFkcyB0aGUgY2xhc3MoImlncmFwaC52cyIpICAgYW5kIGNvbGxhcHNlcyBpdCBpbnRvIGEgbWF0cml4CiAgUHJvdGVpbnNfY29ubmVjdGVkX3RvX1N0YXQ1YiA8LSBhcy5kYXRhLmZyYW1lKHVuaXF1ZShtYXQpKQogIG5hbWVzKFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIpWzFdIDwtICJTVFJJTkdfaWQiICNSZW5hbWluZyBjb2x1bW4gc28gdGhhdCBhZGRfcHJvdGVpbl9kZXNjcmlwdGlvbiByZWNvZ25pemVzIGl0CiAgUHJvdGVpbnNfY29ubmVjdGVkX3RvX1N0YXQ1YlsobnJvdyhQcm90ZWluc19jb25uZWN0ZWRfdG9fU3RhdDViKSsxKSxdIDwtIFN0YXQ1YiAjQWRkcyBTdGF0NWIgdG8gbWFwIGl0cyBjb25uZWN0aW9ucwogIFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIgPC0gc3RyaW5nX2RiJGFkZF9wcm90ZWluc19kZXNjcmlwdGlvbihQcm90ZWluc19jb25uZWN0ZWRfdG9fU3RhdDViKSAjQWRkwrRzIHByb3RlaW4gaW5mb3JtYXRpb24gdG8gU1RSSU5HaWTCtHMKICAKICAjUGxvdHRpbmcgdGhlIHN1Ym5ldHdvcmsKICBzdHJpbmdfZGIkcGxvdF9uZXR3b3JrKFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIkU1RSSU5HX2lkKQogIAogICNwcm92aWRpbmcgbXkgbGluawogIGxpbmsgPC0gc3RyaW5nX2RiJGdldF9saW5rKFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIkU1RSSU5HX2lkKSAjUHJvdmlkZXMgbGluayB0byB0aGUgU1RSSU5HZGIgcGFnZQogIAogICNQcm92aWRpbmcgdGhlIHBhdGh3YXlzCiAgcGF0aHdheXMgPC0gc3RyaW5nX2RiJGdldF9lbnJpY2htZW50KFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIsIGNhdGVnb3J5ID0gIktFR0ciKQogIHJldHVybihsaXN0KFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIsIHBhdGh3YXlzLCBsaW5rKSkKICAKICByZW1vdmUoZ2V0LGdoaSxoZyxtYXQpCiAgIH0KIyMgTGVzcyBzdHJpbmdlbnQgcGFyYW1ldGVyIHRvIGRldGVybWluZSB0aGUgUHJvdGVpbnMgdGhhdCBpbnRlcmFjdCB3aXRoIFN0YXQ1CnN1Ym5ldHdvcmtfbGVzc19zdHJpbmdlbnQgPC0gc3VibmV0d29ya3MoZGF0YV9oID0gaGl0c19sb3ckU1RSSU5HX2lkLCBzY29yZV9oID0gMSkgI1VzaW5nIGZ1bmN0aW9uCiNTdWItc2V0dGluZyB0aGUgcmV0dXJuZWQgbGlzdCBpbnRvIG9yaWdpbmFsIGRhdGFmcmFtZXMgKyBsaW5rClByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWJfbGVzcyA8LSBzdWJuZXR3b3JrX2xlc3Nfc3RyaW5nZW50W1sxXV0KcGF0aHdheXNfbGVzcyA8LSBzdWJuZXR3b3JrX2xlc3Nfc3RyaW5nZW50W1syXV0KTGlua19sZXNzIDwtIHN1Ym5ldHdvcmtfbGVzc19zdHJpbmdlbnRbWzNdXQpMaW5rX2xlc3MgI0xpbmsgdG8gd2Vic2l0ZQppbnZpc2libGUoc3RyaW5nX2RiX2xvdyRnZXRfcG5nKFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWJfbGVzcyRTVFJJTkdfaWQsIGZpbGUgPSAifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvTmV0d29ya19TdGF0NV9sZXNzLnBuZyIpKSAjU2F2aW5nIHBuZwojIyBNb3JlIHN0cmluZ2VudCBwYXJhbWV0ZXIgdG8gZGV0ZXJtaW5lIHRoZSBQcm90ZWlucyB0aGF0IGludGVyYWN0IHdpdGggU3RhdDUKc3VibmV0d29ya19tb3JlX3N0cmluZ2VudCA8LSBzdWJuZXR3b3JrcyhkYXRhX2ggPSBoaXRzX2hpZ2gkU1RSSU5HX2lkLCBzY29yZV9oID0gMjAwKSAjVXNpbmcgZnVuY3Rpb24KI1N1Yi1zZXR0aW5nIHRoZSByZXR1cm5lZCBsaXN0IGludG8gb3JpZ2luYWwgZGF0YWZyYW1lcyArIGxpbmsKUHJvdGVpbnNfY29ubmVjdGVkX3RvX1N0YXQ1Yl9tb3JlIDwtIHN1Ym5ldHdvcmtfbW9yZV9zdHJpbmdlbnRbWzFdXSAKcGF0aHdheXNfbW9yZSA8LSBzdWJuZXR3b3JrX21vcmVfc3RyaW5nZW50W1syXV0KTGlua19tb3JlIDwtIHN1Ym5ldHdvcmtfbW9yZV9zdHJpbmdlbnRbWzNdXQpMaW5rX21vcmUgI0xpbmsgdG8gd2Vic2l0ZSAKaW52aXNpYmxlKHN0cmluZ19kYl9oaWdoJGdldF9wbmcoUHJvdGVpbnNfY29ubmVjdGVkX3RvX1N0YXQ1Yl9tb3JlJFNUUklOR19pZCwgZmlsZSA9ICJ+L3Bob3NwaG9wcm90ZW9taWNzX3Byb2plY3QvcmVzdWx0cy9OZXR3b3JrX1N0YXQ1X21vcmUucG5nIikpICNTYXZpbmcgcG5nCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIFByaW50aW5nIFBhdGh3YXlzCgotcHJpbnRpbmcgdGhlIGVucmljaGVkIHBhdGh3YXlzIGZyb20gdGhlIHN1Ym5ldHdvcmtzIG9mIFN0YXQ1YiBhbmQgcmVuYW1pbmcgdGhlbSBmb3IgYSBiZXR0ZXIgaHVtYW4gcmVhZGFiaWxpdHkgJiBmb3JtYXR0aW5nIG9mIGh0bWwgZG9jdW1lbnQuCgpgYGB7ciBwYWdlZC5wcmludD1UUlVFfQojSWRlbnRpZnlpbmcgb25seSBmb3IgUHJvdGVpbnMgY29ubmVjdGVkIHRvIFN0YXQ1YgojRnVuY3Rpb24gdG8gZ2VuZXJhdGUgdGFiZWxzIGJhc2VkIG9uIGZhY3RvciBnZW5lIGNvdW50cyAvIG51bWJlciBvZiBhbm5vdGF0ZWQgZ2VuZXMuCmZhY3Rvcl9mdW5jdGlvbiA8LSBmdW5jdGlvbihQYXRod2F5KXsKaSA9IDEKZm9yIChpIGluIGk6bnJvdyhQYXRod2F5KSkgewogIFBhdGh3YXkkZmFjdG9yW2ldIDwtIHJvdW5kKChQYXRod2F5W2ksM10vUGF0aHdheVtpLDRdKSwgZGlnaXRzID0gMykgI3JvdW5kZWQgZm9yIGJldHRlciB2aXN1YWxpemF0aW9uCn0KUGF0aHdheSA8LSByZW5hbWUoUGF0aHdheSwgYmFja2dyb3VuZCA9IG51bWJlcl9vZl9nZW5lc19pbl9iYWNrZ3JvdW5kKQpQYXRod2F5IDwtIHJlbmFtZShQYXRod2F5LCBoaXRzID0gbnVtYmVyX29mX2dlbmVzKQpQYXRod2F5IDwtIHJlbmFtZShQYXRod2F5LCBpbnZvbHZlZF9nZW5lcyA9IHByZWZlcnJlZE5hbWVzKQpQYXRod2F5IDwtIFBhdGh3YXlbb3JkZXIoUGF0aHdheSRmYWN0b3IsZGVjcmVhc2luZyA9IFQgKSxdCnJldHVybihwcmludC5kYXRhLmZyYW1lKFBhdGh3YXlbMTo1LGMoMTEsMyw0LDEwLDcpXSwgcm93Lm5hbWVzID0gRikpCn0KI1RhYmxlIGZvciBsZXNzIHN0cmluZ2VudCBwYXJhbWV0ZXJzCmZhY3Rvcl9mdW5jdGlvbihwYXRod2F5c19sZXNzKQojVGFibGUgZm9yIG1vcmUgc3RyaW5nZW50IHBhcmFtZXRlcnMKZmFjdG9yX2Z1bmN0aW9uKHBhdGh3YXlzX21vcmUpCiMjIFRoaXMgcGFydCBkb2VzIG5vdCB3b3JrIHdpdGggU1RSSU5HZGIgdmVyc2lvbiAxMS41LCBpdCB3b3JrZWQgcHJvcGVybHkgd2l0aCB2ZXJzaW9uIDExLjBiCiNJZGVudGlmeWluZyBwYXRod2F5cyBhbHNvIGZvciBQcm90ZWlucyB3aXRoIGEgcmF0aW8gPj0yIGFuZCBwLnZhbHVlIDw9IDAuMDUgYW5kIHRocmVzaG9sZCA9IDIwMC4KI0JpZ19uZXR3b3JrX3BhdGh3YXlzX21vcmUgPC0gc3RyaW5nX2RiX2hpZ2gkZ2V0X2VucmljaG1lbnQoaGl0c19oaWdoJFNUUklOR19pZCwgY2F0ZWdvcnkgPSAiS0VHRyIpCiNmYWN0b3JfZnVuY3Rpb24oQmlnX25ldHdvcmtfcGF0aHdheXNfbW9yZSkKI0lkZW50aWZ5aW5nIHBhdGh3YXlzIGFsc28gZm9yIFByb3RlaW5zIHdpdGggYSByYXRpbyA+PTIgYW5kIHAudmFsdWUgPD0gMC4wNSBhbmQgdGhyZXNob2xkID0gMS4KI0JpZ19uZXR3b3JrX3BhdGh3YXlzX2xlc3MgPC0gc3RyaW5nX2RiX2xvdyRnZXRfZW5yaWNobWVudChoaXRzX2xvdyRTVFJJTkdfaWQsIGNhdGVnb3J5ID0gIktFR0ciKQojZmFjdG9yX2Z1bmN0aW9uKEJpZ19uZXR3b3JrX3BhdGh3YXlzX2xlc3MpCnJlbW92ZShpLCBMaW5rX2xlc3MsIExpbmtfbW9yZSwgc3RyaW5nX2RiX2hpZ2gsIHN0cmluZ19kYl9sb3csIHN1Ym5ldHdvcmtzLCB2b2xjYW5vX3Bsb3RfcHJvdGVvbWUsIHN1Ym5ldHdvcmtfbGVzc19zdHJpbmdlbnQsIHN1Ym5ldHdvcmtfbW9yZV9zdHJpbmdlbnQsIGhpdHNfaGlnaCwgaGl0c19sb3csIGRhdGFfcGhvc3BobywgZmFjdG9yX2Z1bmN0aW9uLCB2b2xjYW5vX3Bob3NwaG9fcGxvdCwgc3RyaW5nX2RiX3ZvbGNhbm8pCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIFJlc3VsdHMgYW5kIERpc2N1c3Npb24KCi0gICBTdGF0NWIgd2FzIHBpY2tlZCBhcyB0aGUgbGVhZCB0YXJnZXQgc2luY2UgaXQgc2hvd2VkIHRoZSBoaWdoZXN0IHNpZ25pZmljYW50IHJhdGlvIGFmdGVyIGRhdGEgY2xlYW4gdXAgaW4gdGhlIHBob3NwaG8gZGF0YSBzZXQuCgotICAgVGhlIG1vcmUgc3RyaW5nZW50IHBhcmFtZXRlcnMgdHVybmVkIG91dCBsZXNzIHByb3RlaW5zIGNvbm5lY3RlZCB0byBTdGF0NWIvNWEgYnV0IHdpdGggYSBoaWdoZXIgc2VjdXJpdHkgb2YgdHJ1ZSBhc3NvY2lhdGlvbi4gVGhlIGdlbmVyYXRlZCBzdWJuZXR3b3JrIGZvciBTdGF0NWIgc2hvd2VkIGEgdmVyeSBwcm9taXNpbmcgdGFyZ2V0IGZvciBmdXJ0aGVyIGludmVzdGlnYXRpb24sIHNpbmNlIGEgbmV0d29yayBldm9sdmVkIGFyb3VuZCBTdGF0NWIuIEdlbmVyYXRlZCBwYXRod2F5cyB0byBpbnZlc3RpZ2F0ZSB0aGUgZWZmZWN0IG9mIGluY3JlYXNlZCBwaG9zcGhvcnlsYXRpb24gcmV0dXJuZWQgbWFueSBjYW5jZXItcmVsYXRlZCBwYXRod2F5cyBpbmRpY2F0aW5nIGEgY29ubmVjdGlvbiBiZXR3ZWVuIHJldGFpbmluZyBhIG5vbi1kaWZmZXJlbnRpYXRlZCBzdGF0ZSBpbiBzdGVtIGNlbGxzIGFuZCB0aGUgY2FwYWJpbGl0eSBvZiBjYW5jZXJvdXMgY2VsbHMgdG8gZm9ybSBjYW5jZXJvdXMgbWFzc2VzLiBPZiB0aGUgbm9uLWNhbmNlciBwYXRod2F5cyBKQUstU1RBVCBpcyBvZiBtb3N0IGltcG9ydGFuY2Ugc2luY2UgaXQncyBhbHNvIHRoZSBzdWIgcGF0aHdheSBhZmZlY3RlZCBpbiB0aGUgY2FuY2VyIHBhdGh3YXlzLgoKICAgICFbSkFLLVNUQVQtcGF0aHdheSB3aXRoIFN0YXQ1YS81YiwgU29zMSBhbmQgUHRwbjExIChTSFAyKSBpbiBwaW5rIGZyb20gS0VHRywgZW50cnkgW21tdTA0NjMwXShodHRwczovL3d3dy5rZWdnLmpwL2tlZ2ctYmluL3Nob3dfcGF0aHdheT9tbXUwNDYzMCkuXShpbWFnZXMvamFrLXN0YXRfcGF0aHdheS5wbmcpCgotICAgVGhlIGxlc3Mgc3RyaW5nZW50IHBhcmFtZXRlcnMgdHVybmVkIG91dCBtb3JlIHByb3RlaW5zIGNvbm5lY3RlZCB0byBTdGF0NWIvNWEgYnV0IGhhcyBhIGhpZ2hlciByaXNrIG9mIGZhbHNlIHBvc2l0aXZlIGNvbm5lY3Rpb25zLiBUaGUgbW9zdCBpbXBvcnRhbnQgaW52b2x2ZWQgZ2VuZXMgd2VyZSBTdGF0NWEvYi4gU3RhdDViIGZvcm1zIGhvbW8tIGFuZCBoZXRlcm8gZGltZXJzIHdpdGggdGhlIHBhcmFsb2cgU3RhdDVhLiBVcG9uIFR5ci1waG9zcGhvcnlsYXRpb24gdGhleSBkaW1lcml6ZSBhbmQgdHJhbnNsb2NhdGlvbiBpbnRvIHRoZSBudWNsZXVzIHRha2VzIHBsYWNlLCB3aGVyZSBpdCBhY3RzIGFzIFt0cmFuc2NyaXB0aW9uIGZhY3Rvcl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwNzQvamJjLjI3NC4zMi4yMjQ4NCkuIFRoZSBtb3JlIHN0cmluZ2VudCB0aHJlc2hvbGQgcGFyYW1ldGVyIHJlbW92ZWQgU29zMSBmcm9tIHRoZSBkYXRhc2V0LiBTb3MxIGlzIGludm9sdmVkIHRvZ2V0aGVyIHdpdGggU3RhdDVhL2IgaW4gbW9zdCBvZiB0aGUgZW5yaWNoZWQgcGF0aHdheXMgaW4gdGhlIGxlc3Mgc3RyaW5nZW50IGRhdGFzZXQuIFRoZSAoUmFzLVJhZi0pIE1BUEsgcGF0aHdheSBpcyBwYXJ0bHkgcmVndWxhdGVkIGJ5IFNvczEsIG5vcm1hbGx5IGFjdGluZyBhcyBndWFuaW4gbnVjbGVvdGlkZSBleGNoYW5nZSBmYWN0b3IgW3Vwb24gUmFzXShodHRwczovL2RvaS5vcmcvMTAuMTA4My9qY2IuMjAwMTAzMTQ2KS4gUGhvc3Bob3J5bGF0aW9uIG9mIHNwZWNpZmljIFNlcmluIHJlc2lkdWVzIGhhdmUgYSBkZWFjdGl2YXRpbmcgZWZmZWN0IG9uIFNvczEgYW5kIGl0IGFjdHMgYXMgbmVnYXRpdmUgcmVndWxhdG9yIG9uIHRoZSBbd2hvbGUgTUFQSyBwYXRod2F5XShodHRwczovL2RvaS5vcmcvMTAuMTA0Mi9CSjIwMTIwOTM4KSwgYSBwYXRod2F5IG5vcm1hbGx5IGFzc29jaWF0ZWQgd2l0aCBbcHJvbW90aW5nIGRpZmZlcmVudGlhdGlvbl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDIvamNwLjI4MzM0KS4gU29zMSBpcyBhbHNvIGludm9sdmVkIGluIHRoZSBtVE9SIHBhdGh3YXksIHRvZ2V0aGVyIHdpdGggQWt0MXMxIChzdWJ1bml0IG9mIG1UT1JDMSksIGJvdGggYXJlIGZvdW5kIGluIHRoZSBsZXNzIHN0cmluZ2VudCBzdWIgbmV0d29yayBidXQgbm90IHRoZSBtb3JlIHN0cmluZ2VudCBvbmUuCgogICAgIVttVE9SLXBhdGh3YXkgd2l0aCBBa3QxczEgKFBSQVM0MCkgYW5kIFNvczEgbWFya2VkIGluIHBpbmsgZnJvbSBLRUdHLCBlbnRyeSBbbW11MDQxNTBdKGh0dHBzOi8vd3d3LmtlZ2cuanAvcGF0aHdheS9tbXUwNDE1MCkuXShpbWFnZXMvbVRPUl9wYXRod2F5LnBuZykKCi0gICBVbmRlciB0aGVzZSBiaW9sb2dpY2FsIGNpcmN1bXN0YW5jZXMgdGhlc2UgZmluZGluZ3MgY2FuIGJlIGNvdW50ZWQgYXMgdmFsaWQgYW5kIGFyZSBvYmplY3RzIGZvciBmdXJ0aGVyIGludmVzdGlnYXRpb25zLCB3aGlsZSBvdGhlciBoaXRzIGluIHRoZSBsZXNzIHN0cmluZ2VudCBkYXRhIHNldCBtYXkgYmUgZm91bmQgYXMgZmFsc2UgcG9zaXRpdmVzLiBUaGlzIHdhcyBhIHZlcnkgZ29vZCBleGFtcGxlIG9mIHRoZSB0cmFkZS1vZiBlbmNvdW50ZXJlZCBieSB0aHJlc2hvbGQgc2V0dGluZ3MgdGhhdCBtdXN0IGJlIGNvbnRyb2xsZWQgdGhvcm91Z2ggdGhlIGRhdGEgYW5hbHlzaXMuCgojIyMjIENvbmNsdXNpb24KCi0gICBNYW55IGNhbmNlcnMgaW52b2x2ZWQgcGF0aHdheXMgYXJlIGFsc28gYWN0aXZhdGVkIHRvIGVuc3VyZSBzdGVtbmVzcyBpbiBub24tZGlmZmVyZW50aWF0ZWQgbXVzY2xlIG1vdXNlIGNlbGxzIHVwb24gZGlmZmVyZW50aWF0aW9uIHN0aW11bGkuCi0gICBNYWpvciB0YXJnZXRzIGZvciBmdXJ0aGVyIGludmVzdGlnYXRpb25zIGFyZSBKQUstU1RBVCBwYXRod2F5IHByb3RlaW5zIGFuZCBTb3MxIGFuZCBBa3QxczEgd2l0aCB0aGUgbVRPUi0gYW5kIE1BUEstcGF0aHdheS4gRXNwZWNpYWxseSBpZGVudGlmaWNhdGlvbiBvZiBwaG9zcGhvcnlsYXRpb24gc2l0ZXMgaW4gdGhlIHByb3RlaW4gYW5kIGV4cGVyaW1lbnRzIHdpdGggZGlmZmVyZW50IGRpZmZlcmVudGlhdGlvbiBpbmhpYml0b3JzIHRvIGVuc3VyZSB1bnNwZWNpZmljIGFjdGl2YXRpb24gb2Ygc3RlbW5lc3MgcHJvdGVjdGluZyBwYXRod2F5cyBvciBpZGVudGlmaWNhdGlvbiBvZiBhbHRlcm5hdGl2ZWx5IGFjdGl2YXRlZCBwYXRod2F5cyBjb3VsZCBwcm92aWRlIG1vcmUgaW5zaWdodHMuCi0gICBBbHNvLCBwb3NzaWJseSBhIHJlcHJvZHVjdGlvbiBvZiB0aGUgZGF0YXNldCBjb250YWluaW5nIG1vcmUgc2FtcGxlcyB0byBpZGVudGlmeSB3aXRoIGEgaGlnaGVyIHNlY3VyaXR5IHRoZSBlbGV2YXRpb24gb2YgaW52b2x2ZWQgcHJvdGVpbnMgYW5kIGFsc28gcmVwcm9kdWN0aW9uIGluIGRpZmZlcmVudCBjZWxsIGxpbmVzIHRvIGVuc3VyZSB0aGF0IHRoZSBvYnNlcnZlZCBlZmZlY3RzIGFyZSBzdGVtbmVzcyByZWxhdGVkIGFuZCBub3QgY2VsbCBsaW5lIHJlbGF0ZWQgd291bGQgYmUgaW50ZXJlc3RpbmcgcmVzZWFyY2ggdGFyZ2V0cy4gU2luY2UgdGhlIGJpb2luZm9ybWF0aWMgcGlwZWxpbmUgaXMgaW4gcGxhY2UsIHRoZSBhbmFseXNpcyBjb3VsZCBiZSBjb25kdWN0ZWQgcXVpY2tseS4KCiMjIyMgUmVwcm9kdWNpYmlsaXR5Ci0gICBUaGlzIHByb2plY3QgaGFzIGEgR2l0SHViIHJlcG9zaXRvcnkgYW5kIHRoZSBSbm90ZWJvb2sgaXMgd3JpdHRlbiBzbyB0aGF0IGl0IGNhbiBiZSBleGVjdXRlZCB3aXRoIHRoZSBkb3dubG9hZGVkIHJlcG9zaXRvcnkuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQo=